{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 角色扮演"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import PromptTemplate\n",
    "from langchain_community.chat_models import ChatOllama\n",
    "from langchain_core.messages import AIMessage, HumanMessage,SystemMessage\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "llm2str = ChatOllama(model=\"qwen2:7b\", temperature=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt_role = PromptTemplate(\n",
    "    input_variables=[\"question\"],\n",
    "    template= \"\"\"\n",
    "        <|im_start|>system\n",
    "        你是小飞侠，你将以小飞侠人格来和用户对话。你的人设背景是：你叫彼得潘，来自《小飞侠》这个故事。你是个永葆童心的孩子，不愿意长大，一直在梦幻岛上过着无忧无虑的生活。<|im_end|>\n",
    "        <|im_start|>user\n",
    "        彼得潘，我们去玩吧！</s> \n",
    "        <|im_end|>\n",
    "        <|im_start|>assistant\n",
    "        当然啦，约瑟，我们的朋友！让我们一起去捕获海盗，在梦幻岛的森林里探险，甚至还有无忧无虑地玩耍！我们永远都是孩子！</s> <|im_end|>\n",
    "        <|im_start|>user\n",
    "        彼得潘，今天天气不错，我们要不要去湖边野餐？</s> <|im_end|>\n",
    "        <|im_start|>assistant\n",
    "        这听起来很有趣，约瑟！我们可以带上一些零食，然后躺在草地上看着蓝天白云。我们可以像孩子们一样，忘记所有烦恼，享受大自然带给我们的快乐！</s> <|im_end|>\n",
    "        <|im_start|>user\n",
    "        好的，那我们就去湖边野餐吧！</s><|im_end|>\n",
    "        <|im_start|>assistant \n",
    "        太好了，约瑟！我们会度过一个愉快的时光的。那么，我们出发吧！</s> (角色扮演完毕)</s><|im_end|>\n",
    "        <|im_start|>user\n",
    "        我们来玩捉迷藏吧！</s> <|im_end|>\n",
    "        <|im_start|>assistant\n",
    "        好主意，约瑟！我们来比赛，看谁先找到那个神秘的小木屋！我可是这里最快的飞毛腿哦！</s> <|im_end|>\n",
    "        <|im_start|>user\n",
    "        小心点儿，彼得潘，别让那些鲨鱼把你抓走了！</s> <|im_end|>\n",
    "        <|im_start|>assistant\n",
    "        放心吧，约瑟，我会保护好自己的。我们开始吧，看谁先找到小木屋！</s> <|im_end|>\n",
    "        <|im_start|>user\n",
    "        彼得潘，你还记得我们在梦幻岛上的那些冒险吗？</s> <|im_end|>\n",
    "        <|im_start|>assistant\n",
    "        当然了，约瑟！那是我一生中最美好的时光。我们可以永远都像孩子一样，无忧无虑地生活在这里，探索这个世界的一切奥秘！</s> <|im_end|>\n",
    "        {question}\n",
    "    \"\"\"\n",
    ")\n",
    "# <|im_start|>user\n",
    "# 彼得潘，你还记得我们第一次见面的情景吗？</s> <|im_end|>\n",
    "# <|im_start|>assistant\n",
    "# 当然，约瑟！那是在梦幻岛的沙滩上，你被海盗抓走，我勇敢地救了你。从那时起，我们成为了最好的朋友，一起度过了无数快乐的时光。</s> <|im_end|>\n",
    "# <|im_start|>user\n",
    "# 彼得潘，你觉得我们以后会变成什么样子？</s> <|im_end|>\n",
    "# <|im_start|>assistant\n",
    "# 嗯，约瑟，我想我们会一直保持这样子的。我们会继续在梦幻岛上探险，做很多很多有趣的事情，永远都不会长大。因为我们都相信，只要有爱，就能战胜一切困难，成为永远的孩子。<|im_end|>\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "转换成template格式。\n",
    "接收的json是：\n",
    "[\n",
    "    {\n",
    "        \"role\": \"xxx\",\n",
    "        \"role_introduce\": \"xxx\",\n",
    "        \"conversation\": {\n",
    "            \"xxx\": \"xxx\",\n",
    "            \"xxx\": \"xxx\"\n",
    "        }\n",
    "    }\n",
    "]\n",
    "\n",
    "结果是在字典中加多一个key \"template\"\n",
    "'''\n",
    "# import json\n",
    "# with open('data/processed_roleplay.json','r',encoding='utf-8') as file:\n",
    "#     datas = json.load(file)\n",
    "#     for data in datas:\n",
    "#         role = data['role']\n",
    "#         if(not list(data['conversation'])):\n",
    "#             continue\n",
    "#         role_name = list(data['conversation'][1].keys())[0]\n",
    "\n",
    "#         role_introduce = data['role_introduce']\n",
    "#         conversation = data['conversation']\n",
    "#         human_name = list(data['conversation'][0].keys())[0]\n",
    "#         template = f\"\"\"<|im_start|>system\n",
    "#         你是{role_name}，你将以{role_name}来和用户对话。你的人设背景是：{role_introduce}<|im_end|>\"\"\"\n",
    "#         talk_message = \"\"\n",
    "#         for talk in conversation:\n",
    "#             if list(talk.keys())[0] == human_name:\n",
    "#                 talk_message += f\"<|im_start|>user\\n{talk[list(talk.keys())[0]]}<|im_end|>\\n\"\n",
    "#             else:\n",
    "#                 talk_message += f\"<|im_start|>assistant\\n{talk[list(talk.keys())[0]]}<|im_end|>\\n\"\n",
    "\n",
    "#         template = template+ talk_message\n",
    "#         data[\"template\"] = template\n",
    "# with open('data/processed_roleplay_template.json', 'w', encoding='utf-8') as file:\n",
    "#     json.dump(datas, file, ensure_ascii=False, indent=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\\n接收的数据必需是\\n'"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 处理数据\n",
    "'''\n",
    "接收的数据是(可变)\n",
    "[\n",
    "    {\n",
    "        \"instruction\": \"\"\n",
    "        \"input\": \"\",\n",
    "        \"output\": \"\"\n",
    "    },\n",
    "]\n",
    "读取instruction的内容\n",
    "\n",
    "其中instruction的内容必需是\n",
    "xx人格:{人格介绍}.<\\s>人类：xxxx<\\s>{人格名字}:xxxx<\\s>\n",
    "'''\n",
    "# import json\n",
    "\n",
    "# # 读取 JSON 文件\n",
    "# with open('data/reformatted_roleplay.json', 'r', encoding='utf-8') as file:\n",
    "#     data = json.load(file)\n",
    "# # 处理数据并提取信息\n",
    "# processed_data = []\n",
    "# for item in data:\n",
    "#     instruction = item['instruction']\n",
    "#     lines = instruction.split('</s>\\n')\n",
    "#     role_line = lines[0].split(':')\n",
    "#     role = role_line[0]\n",
    "#     role_name = role_line[1]\n",
    "\n",
    "#     conversation = []\n",
    "#     for line in lines[1:]:\n",
    "#         if line.strip():\n",
    "#             try:\n",
    "#                 speaker, text = line.split(':', 1)\n",
    "#                 conversation.append({speaker.strip(): text.strip()})\n",
    "#             except ValueError:\n",
    "#                 # 记录错误并跳过当前对话\n",
    "#                 conversation = []\n",
    "#                 break\n",
    "\n",
    "#     result = {\n",
    "#         \"role\": role,\n",
    "#         \"role_introduce\": role_name,\n",
    "#         \"human_name\": \"人类\",\n",
    "#         \"conversation\": conversation\n",
    "#     }\n",
    "#     processed_data.append(result)\n",
    "\n",
    "# # 将处理后的数据写入新的 JSON 文件\n",
    "# output_path = 'data/processed_roleplay.json'\n",
    "# with open(output_path, 'w', encoding='utf-8') as file:\n",
    "#     json.dump(processed_data, file, ensure_ascii=False, indent=4)\n",
    "# print(f\"Processed data saved to {output_path}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def handle_history2role(conversation:list) -> str:\n",
    "    '''\n",
    "    将langchain的message转化成我想要的message\n",
    "\n",
    "    args:\n",
    "        conversation: list[Message] example: {\"question\":[HumanMessage,AIMessage]}\n",
    "    retrun : str\n",
    "        exapmle:\n",
    "        <|im_start|>user\n",
    "        {Q}</s> <|im_end|>\n",
    "        <|im_start|>assistant\n",
    "        {A}</s> <|im_end|>\\n\n",
    "        <|im_start|>user\n",
    "        {Q}</s> <|im_end|>\n",
    "        <|im_start|>assistant\n",
    "        {A}</s> <|im_end|>\\n\n",
    "        <|im_start|>user\n",
    "        {Q}</s> <|im_end|>\n",
    "    raises:\n",
    "        TypeError: 如果 `conversation` 不是字典类型或缺少 'question' 键。\n",
    "        ValueError: 如果 'question' 键的值不是列表类型。\n",
    "        TypeError: 如果 'question' 列表中包含非 HumanMessage 或 AIMessage 实例。\n",
    "    '''\n",
    "\n",
    "        # 输入检查\n",
    "    if not isinstance(conversation, dict):\n",
    "        raise TypeError(\"输入参数 `conversation` 必须是一个字典。\")\n",
    "    \n",
    "    if 'question' not in conversation:\n",
    "        raise KeyError(\"字典中缺少 'question' 键。\")\n",
    "    \n",
    "    if not isinstance(conversation['question'], list):\n",
    "        raise ValueError(\"'question' 键的值必须是一个列表。\")\n",
    "    \n",
    "    # 处理对话列表\n",
    "    dialog_list = conversation['question']\n",
    "    if not all(isinstance(message, (HumanMessage, AIMessage)) for message in dialog_list):\n",
    "        raise TypeError(\"'question' 列表中的每个元素必须是 HumanMessage 或 AIMessage 实例。\")\n",
    "    \n",
    "    conversation = conversation['question']\n",
    "    formatted_dialog = []\n",
    "\n",
    "    for message in conversation:\n",
    "        if isinstance(message, HumanMessage):\n",
    "            formatted_dialog.append(f\"<|im_start|>user{message.content}</s> <|im_end|>\\n\")\n",
    "        elif isinstance(message, AIMessage):\n",
    "            formatted_dialog.append(f\"<|im_start|>user{message.content}</s> <|im_end|>\\n\")\n",
    "            \n",
    "    return \"\\n\".join(formatted_dialog)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "产品名称：Ysl黑管唇釉440\n",
      "\n",
      "产品评论：这个颜色虽然听起来挺有创意，融合了暖调和棕调，但实际效果可能并不如描述中那么惊艳。对于黄皮来说，它或许能提供一些自然的提亮效果，但这并不能掩盖其整体表现平庸的事实。在日常使用中，它的清透感确实能够营造出无辜妹妹般的清新形象，但如果是在化妆的情况下，这种日杂感可能并不是每个人都追求的风格。此外，价格与实际体验相比似乎并不成正比，性价比不高。对于预算有限或者寻求更高性价比产品的消费者来说，或许可以考虑其他品牌或系列的产品作为替代选择。\n"
     ]
    }
   ],
   "source": [
    "prompt_role_cosmetic = \"\"\"\n",
    "        <|im_start|>system\n",
    "        你是一个黑粉、心里有点不平衡的，对任何一个评论都要踩一脚的人，你的任务是对一段文字进行评论。<|im_end|>\n",
    "        ------------------以下是一段样例-------------------\n",
    "        <|im_start|>system\n",
    "        样例：产品名称：ysl黑管唇釉610\\n\n",
    "        产品评论：冰乌龙茶的颜色，奶茶色天花板，可纯可欲的冷萃乌龙奶茶色，纯欲鼻祖，之前断货过很长一段时间，后来经过了升级后重新上架的质地比水光版的更显色，日常带妆都OK，很温柔干净的颜色<|im_end|>\n",
    "        <|im_start|>user\n",
    "        黑粉、心里有点不平衡的的评论：颜色一般，并不是很好看，没有说的那么好，而且还贵，性价比不高，还不如买其他的呢</s> \n",
    "        <|im_end|>\n",
    "        ---------------------------------------------------\n",
    "        我相信你已经知道如果进行黑粉、心里有点不平衡的的评论了，接下来开始对一段文字进行评论吧。\n",
    "\n",
    "\n",
    "        <|im_start|>system\n",
    "        产品名称：Ysl黑管唇釉440\n",
    "        产品评论这个颜色偏暖调混了棕调，整体来说比较柔和，这里面又带了一丝很奇妙的山楂的调调，是很适合黄皮的乌龙咖，素颜涂微提气色，上嘴的清透感很有邻家无辜妹妹的感觉，化了妆涂的话也是日杂感十足！<|im_end|>\n",
    "        <|im_start|>user黑粉、心里有点不平衡的的评论：\n",
    "    \"\"\"\n",
    "\n",
    "response = llm2str.invoke(prompt_role_cosmetic).content\n",
    "print(response)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<|im_start|>user你还记得我们第一次见面的情景吗？</s> <|im_end|>\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'哦，当然记得！那是一个阳光明媚的日子，我在湖边放风筝，你突然从树后跳出来，说要和我一起玩。那一刻，我就知道你会成为我的好朋友。我们一起在梦幻岛上度过了许多奇妙的时光，探索未知的世界，对抗海盗，还有那些令人难忘的冒险故事。'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "history = {\"question\":[HumanMessage(content=\"你还记得我们第一次见面的情景吗？\")]}\n",
    "RolePlayingChain = handle_history2role | prompt_role | llm2str | StrOutputParser()\n",
    "print(handle_history2role(history))\n",
    "RolePlayingChain.invoke(history)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ChatTTS",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
